/// start 这个方法 if for 嵌套太多了，如何改善好呢？


use crate::datasource::DataSource;
use crate::endpoint::{EndPoint, Token};
use log::error;
use serde::Deserialize;
use serde_yaml::Mapping;
use std::thread;
use ureq::Error;

const BATCH_SIZE: i32 = 50;

/// tasks configuration in indexer.yml [tasks]
#[derive(Deserialize, PartialEq, Debug)]
pub struct Task {
    pub datasource: String,
    pub index: String,
    pub table: Option<String>,
    #[serde(default)]
    pub primary: Option<String>,
    #[serde(default)]
    pub sql: Option<String>,
    pub interval: Option<u64>,
    #[serde(default)]
    pub fields: Mapping,
}

impl Task {
    /// to test deeply
    pub fn test(&self) -> Result<bool, String> {
        Ok(true)
    }

    /// start thread to handle increment update
    pub fn start(&self, name: &String, source: &DataSource, endp: &EndPoint) {
        if let Ok(ids) = source.inst() {
            let mut token = Token::new(); //indexea token
            loop {
                // read tasks from 'indexea_tasks'
                let mut count = 0;
                match ids.tasks(name, &self, BATCH_SIZE) {
                    Ok(mut records) => {
                        count = records.len() as i32;
                        if count > 0 {
                            for mut rec in &mut records {
                                rec.index = self.index.clone();
                            }

                            let mut is_token_ready = true;
                            // refresh when need
                            if token.is_expired() {
                                match endp.token() {
                                    Ok(t) => token = t,
                                    Err(Error::Status(code, resp)) => {
                                        is_token_ready = false;
                                        if let Ok(reason) = resp.into_string() {
                                            error!("failed to request access token with code {}, reason: {:?}", code, reason);
                                        } else {
                                            error!(
                                                "failed to request access token with code {}",
                                                code
                                            );
                                        }
                                    }
                                    Err(Error::Transport(t)) => {
                                        is_token_ready = false;
                                        if let Some(msg) = t.message() {
                                            error!(
                                                "failed to request access token, reason: {}",
                                                msg
                                            );
                                        }
                                    }
                                }
                            }
                            if is_token_ready {
                                // push to endpoint
                                let results = endp.push(&token, &mut records);
                                //write back to datasource
                                if let Err(e) = ids.finish(results) {
                                    error!("failed flush tasks status, reason: {}", e)
                                }
                            }
                        }
                    }
                    Err(e) => {
                        error!("failed to load todo tasks for {}, reason: {}", name, e)
                    }
                }
                if count < BATCH_SIZE {
                    thread::sleep(std::time::Duration::from_millis(self.interval.unwrap()));
                }
            }
        }
    }
}

impl Clone for Task {
    fn clone(&self) -> Task {
        Task {
            datasource: self.datasource.clone(),
            index: self.index.clone(),
            table: self.table.clone(),
            primary: self.primary.clone(),
            sql: self.sql.clone(),
            interval: self.interval.clone(),
            fields: self.fields.clone(),
        }
    }
}
